Hashing Algorithms

The goal is to have a long message as input and produce an output which is much shorter called the hash or message digest. Furthermore, we want it to have certain properties.

  • Input: long message
  • Output: short fixed size block (called hash or message digest)

We want the same input always produces the same output (deterministic)

Exercise

  • A non security related example of hash functions?
  • A security related example of hash functions?

There is a difference between hash function and cryptographic hash functions

Desired properties

  • Pre-image: Given a hash h it is computationally infeasible to find a message m that produces h
  • Second preimage: Given message m, it is computationally infeasible to find a message m’, (m ≠ m’) such that, h(m) = h(m’)
  • Collisions: It is computationally difficult to find any two messages m, m’ (m ≠ m’) such that, h(m) = h(m’)

Examples:

  • Recommended Hash Algorithm (SHA-2, SHA-3) by NIST
  • SHA-1: output 160 bits being phased out (shattered.io)
  • MD2, MD4, and MD5 by Ron Rivest [RFC1319, 1320, 1321]


source: shattered.io


SHA Family

Secure Hash Algorithm (SHA) family, is a series of hashing algorithms. Ranging from SHA-0 to SHA-3. SHA-0 should never be used, it's advised to move from SHA-1 to SHA-2. SHA-3 is the most recent version, published in 2015.

  • SHA-1: Digest size (160), Block size (512)
  • SHA-2: Digest size (224, 256, 384, or 512), Block size (512, 1024)
  • SHA-3: Digest size (224, 256, 384, 512), Block size (1600)

In [ ]:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
import base64 # to produce human readable encoding of the bytes

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"PyCon")
digest.update(b"2017")
msg_digest = digest.finalize()
# Notice the output size of the digest
print ("msg_digest:", len(msg_digest), len(msg_digest) * 8)
print ("base64 encoding:", base64.b64encode(msg_digest))

print()

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"PyCon2017")
msg_digest = digest.finalize()
# Notice the output size of the digest
print ("msg_digest:", len(msg_digest), len(msg_digest) * 8)
print ("base64 encoding:", base64.b64encode(msg_digest))

print()

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"PyCon 2017")
msg_digest = digest.finalize()
# Notice the output size of the digest
print ("msg_digest:", len(msg_digest), len(msg_digest) * 8)
print ("base64 encoding:", base64.b64encode(msg_digest))

Exercise

  • Let's explore what other hashing algorithms are available and use a different one.
  • Change "PyCon2017", to PyCon 2017" and see check the result

Python Builtin Support

"Additional algorithms may also be available depending upon the OpenSSL library that Python uses on your platform. On most platforms the sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_128(), shake_256() are also available."

source: https://docs.python.org/3/library/hashlib.html


In [ ]:
import hashlib
sha256 = hashlib.sha256()
sha256.update(b"PyCon2017")

msg_digest = sha256.digest()
# Notice the output size of the digest
print ("msg_digest:", len(msg_digest), len(msg_digest) * 8)
print ("base64 encoding:", base64.b64encode(msg_digest))

Exercise

  • Let's see what other hashing algorithms our python supports.

Hash-based message authentication code (HMAC)

HMACs are used for message authentications combined with a secret key. This provide integrity checking and authentication. An attacker can not forge the digest without knowing the secret key.

image source: wikipedia

$ HMAC(K,m) = H((K\oplus opad) || H((K \oplus ipad) || m))$

HMAC Calculation


In [ ]:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hmac, hashes
import os
import base64

hmc_key = k = os.urandom(16)
hmc = hmac.HMAC(hmc_key, hashes.SHA1(), default_backend())
hmc.update(b"PyCon2017")
hmc_sig = hmc.finalize()
print (base64.b64encode(hmc_sig))

HMAC Verification


In [ ]:
# Verification Successufl
hmc = hmac.HMAC(hmc_key, hashes.SHA1(), default_backend())
hmc.update(b"PyCon2017")
hmc.verify(hmc_sig)

In [ ]:
# Verification Fails
hmc = hmac.HMAC(hmc_key, hashes.SHA1(), default_backend())
hmc.update(b"PyCon2017")
hmc.verify(hmc_sig+b"1")